大家好,昨天我們用history API來實做簡單的SPA,今天我們要來實做第二種方式,使用location.hash做出SPA。
跟昨天模式一樣,先建立一個HTML後,在body放入一樣的UI:
<ul>
<li><a href="#/Home">主頁</a></li>
<li><a href="#/About">關於</a></li>
<li><a href="#/Contact">聯繫</a></li>
</ul>
<section id="wrapper"></section>
首先說明一下,這邊的超連結屬性因為放入的僅有hash部份,在hash後面多接了斜線與路徑名稱,我們在 [DAY07]SPA路由的應用(下篇) 提到,如此除了可以改變hash值,也不會觸發重新導向請求,所以不需跟昨天一樣,使用監聽點擊事件把超連結預設行為取消。
接著我們來準備script的內容,一樣需要一個判斷路徑對應內容的函式,用來實現Controller與View的功能:
function renderByUrl(url) {
console.log("目前路徑為:", url);
//路徑對應頁面內容
if (url === "#/" || url === "#/Home") {
wrapper.innerHTML = "這是主頁";
} else if (url === "#/About") {
wrapper.innerHTML = "這是關於頁";
} else if (url === "#/Contact") {
wrapper.innerHTML = "這是聯繫頁";
} else {
wrapper.innerHTML = "找不到頁面";
}
}
函式預設url參數我們將會帶入loaction.hash的值,loaction.hash比較特別的是,hash後面有值才會連同#輸出,若是空值或僅有一個#,則僅會返回空值。接著我們需要頁面加載完畢時,判斷目前的hash值來決定內容,所以這邊來加入監聽事件:
//載入事件
window.addEventListener("load", function () {
//使用location.hash判斷頁面內容
renderByUrl(location.hash);
});
最後的是在hash值變化時可以載入對應的內容,這邊事件類型使用hashchange,加入監聽事件:
//監聽網址改變事件
window.addEventListener("hashchange", function () {
//使用location.hash判斷頁面內容
renderByUrl(location.hash);
});
上面的程式碼完成後,我們來看看成果(記得把網址列index.html去掉比較像一般網址):
跟昨天一樣的狀況,主頁預設畫面沒辦法正常顯示,因為在主路徑時沒有hash值,所以也找不到對應的內容。OK,你可能想說,那我們就在renderByUrl函式裡流程控制多加入一個條件,當hash路徑等於空值時內容為主頁,例如這樣:
//判斷目前頁面
function renderByUrl(url) {
console.log("目前路徑為:", url);
//路徑對應頁面內容
if (url === "" || url === "#/" || url === "#/Home") {
wrapper.innerHTML = "這是主頁";
} else if (url === "#/About") {
wrapper.innerHTML = "這是關於頁";
} else if (url === "#/Contact") {
wrapper.innerHTML = "這是聯繫頁";
} else {
wrapper.innerHTML = "找不到頁面";
}
}
雖然情況是解決了,這樣網址列就完全沒有hash,我們希望在預設頁面的網址是#/,一開始使用者在主路徑下且沒有帶任何hash值時,就導向到#/的路徑,這樣才能正確顯示內容。所以稍微取代剛剛的方法,我們一樣在renderByUrl這個函式開頭,加入一段網址重新導向的程式,結果如下:
//判斷目前頁面並渲染
function renderByUrl(url) {
console.log("目前路徑為:", url);
//若沒有hash值預設導向至#/
if (location.hash === "") {
location.href = "#/";
return;
}
//路徑對應頁面內容
if (url === "#/" || url === "#/Home") {
wrapper.innerHTML = "這是主頁";
} else if (url === "#/About") {
wrapper.innerHTML = "這是關於頁";
} else if (url === "#/Contact") {
wrapper.innerHTML = "這是聯繫頁";
} else {
wrapper.innerHTML = "找不到頁面";
}
}
最終結果:
一切都完美運行,而且當用戶輸入對應不到的頁面也能返回自訂找不到頁面的內容,hash就是這麼直接,雖然網址多了一個#,看起來沒那麼優雅,不過效果處理得很OK,不用特別在server端做其他的設置,只有唯一個情況是,若使用者在輸入網址列時,主路徑少了hash並輸入其他對應不到的路徑時,hash就無法起到作用了,這時就還是得依靠server端的設置,統一導向到主頁,並且用剛剛的方式導至帶有hash的主頁。
完成了第二個範例後,對SPA應該有比較完整的認識。恭喜你完成了SPA的試煉,得到至尊大師的稱號,可以下山回家了,以下是任務獎勵:
今日範例完整程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Day09 | Simple SPA with hash</title>
</head>
<body>
<ul>
<li><a href="#/Home">主頁</a></li>
<li><a href="#/About">關於</a></li>
<li><a href="#/Contact">聯繫</a></li>
</ul>
<section id="wrapper"></section>
<script>
//綁定渲染元素
const wrapper = document.querySelector("#wrapper");
//判斷目前頁面
function renderByUrl(url) {
console.log("目前路徑為:", url);
//若沒有hash值預設導向至#/
if (location.hash === "") {
location.href = "#/";
return;
}
//路徑對應頁面內容
if (url === "#/" || url === "#/Home") {
wrapper.innerHTML = "這是主頁";
} else if (url === "#/About") {
wrapper.innerHTML = "這是關於頁";
} else if (url === "#/Contact") {
wrapper.innerHTML = "這是聯繫頁";
} else {
wrapper.innerHTML = "找不到頁面";
}
}
//載入事件
window.addEventListener("load", function () {
//使用location.hash判斷頁面內容
renderByUrl(location.hash);
});
//監聽網址改變事件
window.addEventListener("hashchange", function () {
//使用location.hash判斷頁面內容
renderByUrl(location.hash);
});
</script>
</body>
</html>
…?
…??
…???
...欸等等等,看到這邊你應該會想問,實際開發SPA真的有這麼容易嗎?用這方法來寫根本就是胡鬧吧XD。其實一般專案開發不會把全部程式混和寫在同一頁,這樣既不好維護,主頁檔案也會變得很肥大。身為一個前端工程師,有個強而有力偷懶的工具在身上,也是很合理的事情。所以後續我們要來教大家使用模組化的開發方式,搭配前端開發工具與套件,完整建構出屬於自己的SPA,也是此系列正式實戰應用。